/**********************************************************
 * vcap_buf.c
 *
 *  Copy right (c) 2011, Media Information Systems Inc.
 *
 *  Created on: 2011-11-15
 *      Author: Elvis.zhang
 *     Version: v0.0.0
 *
 *********************************************************/
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/moduleparam.h>
#include <linux/list.h>
#include <linux/cdev.h>
#include <linux/proc_fs.h>
#include <linux/mm.h>
#include <linux/seq_file.h>
#include <linux/ioport.h>
#include <asm/io.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <linux/wait.h>
//#include <asm/semaphore.h>
#include <linux/poll.h>
//#include <asm/arch/mux.h>

#include	"vcap_buf.h"

CAPBUF_Handle CAPBUF_create()
{
	CAPBUF_Handle h;
	h = (CAPBUF_Handle) kmalloc(sizeof(CAPBUF_Obj), GFP_KERNEL);
	h->capBuf = NULL;
	h->rIdx = 0;
	h->wIdx = 0;
	h->nBuf = 0;
	h->bWaitRead = 0;
	h->bWaitWrite = 0;

	init_waitqueue_head(&(h->hWaitRead));
	init_waitqueue_head(&(h->hWaitWrite));

	return h;
}

void CAPBUF_delete(CAPBUF_Handle h)
{
	CAPBUF_setBuf(h, 0, NULL);

	kfree(h);
}

void CAPBUF_setBuf(CAPBUF_Handle h, int nNum, void* ppbuf[])
{
	int i;
	if (h->capBuf != NULL)
	{
		for (i = 0; i < h->nBuf; i++)
		{
			init_MUTEX(&(h->capBuf[i].mutex));
		}

		kfree(h->capBuf);
	}
	if (nNum <= 0)
		return;

	h->capBuf = (PCAPBUFFER) kmalloc(sizeof(CAPBUFFER) * nNum, GFP_KERNEL);
	h->nBuf = nNum;

	for (i = 0; i < h->nBuf; i++)
	{
		init_MUTEX(&(h->capBuf[i].mutex));
		h->capBuf[i].buf = ppbuf[i];
		h->capBuf[i].state = CAPBUF_STATE_INVALID;
	}

	h->rIdx = h->nBuf - 1;
	h->wIdx = h->nBuf - 1;
	h->wIdx_lock = h->wIdx;
	h->wIdx_release = h->wIdx;
}

void* CAPBUF_LockRead(CAPBUF_Handle h, void* timeout)
{
	PCAPBUFFER pBuf = &h->capBuf[h->rIdx];
	if (pBuf->state == CAPBUF_STATE_INVALID)
	{
		if (timeout != NULL)
		{
			h->bWaitWrite = 1;
			wait_event_interruptible(h->hWaitWrite,
					pBuf->state == CAPBUF_STATE_VALID);
			h->bWaitWrite = 0;
		}
		else
		{
			return NULL;
		}
	}

	down_interruptible(&(pBuf->mutex));

	return pBuf->buf;
}

int CAPBUF_UnLockRead(CAPBUF_Handle h)
{
	PCAPBUFFER pBuf = &h->capBuf[h->rIdx];
	if (pBuf->state == CAPBUF_STATE_INVALID)
		return 0;

	pBuf->state = CAPBUF_STATE_INVALID;
	up(&(pBuf->mutex));

	h->rIdx = (h->rIdx + 1) % h->nBuf;
	if (h->bWaitRead)
	{
		wake_up_interruptible(&(h->hWaitRead));
	}

	return 0;
}

void* CAPBUF_LockWrite(CAPBUF_Handle h, void* timeout)
{
	PCAPBUFFER pBuf = &h->capBuf[h->wIdx_lock];
	if (pBuf->state != CAPBUF_STATE_INVALID)
	{
		if (timeout != NULL)
		{
			h->bWaitRead = 1;
			wait_event_interruptible(h->hWaitRead,
					pBuf->state == CAPBUF_STATE_INVALID);
			h->bWaitRead = 0;
		}
		else
		{
			return NULL;
		}
	}

	h->wIdx = h->wIdx_lock;
	h->wIdx_lock = (h->wIdx_lock + 1) % h->nBuf;

	return pBuf->buf;
}

int CAPBUF_UnLockWrite(CAPBUF_Handle h)
{
	PCAPBUFFER pBuf = &h->capBuf[h->wIdx_release];
	if (pBuf->state == CAPBUF_STATE_VALID)
		return 0;

	pBuf->state = CAPBUF_STATE_VALID;

	h->wIdx_release = (h->wIdx_release + 1) % h->nBuf;
	if (h->bWaitWrite)
	{
		wake_up_interruptible(&(h->hWaitWrite));
	}
	return 0;
}
